<?php
/**
 * 列表类
 * 
 * 具体实现请参见 http://www.php.net/~helly/php/ext/spl/
 * 
 * 既可以使用对象的方法，又可以使用[]（一对方括号）访问元素
 * <code>
 * <?php
 * $list = new IArrayList(1, 2, 3, 4, 5);
 * echo $list[2] . "\n"; // =>3
 * echo count($list) . "\n"; // => 5
 * echo isset($list[2]) . "\n"; // => 1
 * unset($list[2]); // unset
 * foreach ($list as $v) { // => 1 2 4 5
 * 	echo $v . "\n";
 * }
 * ?>
 * </code>
 *
 * @version $Id: IArrayList.php 312 2008-04-23 16:29:38Z iwind $
 * @package util.collection
 * @since 0.0.2
 */
class IArrayList extends IAbstractCollection implements ArrayAccess,Countable {
	private $array;
	
	/**
	 * 构造器
	 * 
	 * 可以使用下面方法构造列表：
	 * <code>
	 * <?php
	 * new IArrayList(1);
	 * new IArrayList(1, 2, 3, 4, 5);
	 * new IArrayList(array(1, 2, 3, 4, 5));
	 * ?>
	 * </code>
	 *
	 * @param mixed $array 初始对象
	 */
	public function __construct($array = null) {
		$args = func_get_args();
		$argNum = count($args);
		if ($argNum == 0) {
			$this->array = array();
		}
		else if ($argNum == 1) {
			if (is_array($array)) {
				$this->array = array_values($array);
			}
			else {
				$this->array = $args;
			}
		}
		else {
			$this->array = $args;
		}
	}
	
	/**
	 * 取得迭代器
	 * 
	 * 实现了 IIterable 接口对应方法
	 *
	 * @return IListIterator
	 */
	public function getIterator() {
		return new IListIterator($this->array);
	}
	
	/**
	 * 计算元素个数
	 * 
	 * 实现了 Countable 接口对应方法
	 *
	 * @return integer
	 */
	public function count() {
		return count($this->array);
	}
	
	/**
	 * 判断一个偏移量是否存在
	 *
	 * 实现了 ArrayAccess 接口对应方法
	 * 
	 * @param integer $index
	 * @return boolean
	 */
	public function offsetExists($index) {
		return array_key_exists($index, $this->array);
	}
	
	/**
	 * 从一个偏移量中取得数据
	 * 
	 * 实现了 ArrayAccess 接口对应方法
	 *
	 * @param integer $index 偏移量
	 * @return mixed
	 * @throws ICollectionException 如果偏移量超出列表范围，则抛出异常
	 */
	public function offsetGet($index) {
		if ($this->offsetExists($index)) {
			return $this->array[$index];
		}
		throw new ICollectionException("invalid offset '{$index}' for the list");
	}
	
	/**
	 * 设置偏移量位置上的值
	 *
	 * 实现了 ArrayAccess 接口对应方法
	 * 
	 * @param integer $index 偏移量
	 * @param mixed $item 值
	 */
	public function offsetSet($index, $item) {
		if ($index === null) {
			$this->array[] = $item;
		}
		else {
			$this->insertAt($index, $item);
		}
	}
	
	/**
	 * 删除偏移量位置对应的元素
	 * 
	 * 实现了 ArrayAccess 接口对应方法
	 *
	 * @param integer $index 偏移量
	 * @throws ICollectionException 如果偏移量超出列表范围，则抛出异常
	 */
	public function offsetUnset($index) {
		if ($this->offsetExists($index)) {
			unset($this->array[$index]);
			$this->tidy();
		}
		else {
			throw new ICollectionException("invalid offset '{$index}' for the list");
		}
	}
	
	/**
	 * 加入新的元素
	 *
	 * @param mixed $item 元素值
	 */
	public function add($item1) {
		$item1;
		foreach (func_get_args() as $v) {
			$this->array[] = $v;
		}
	}
	
	/**
	 * 清空列表
	 *
	 */
	public function clear() {
		$this->array = array();
	}
	
	/**
	 * 判断列表中是否有特定的值
	 *
	 * @param mixed $item 值
	 * @return boolean
	 */
	public function contains($item) {
		return in_array($item, $this->array);
	}
	
	/**
	 * 将新的iterable对象的元素加入到当前列表中
	 *
	 * @param mixed $iterable array/IIterable
	 */
	public function addAll($iterable) {
		if(is_array($iterable) || ($iterable instanceof IIterable))	{
			foreach ($iterable as $v) {
				$this->add($v);
			}
		}
		else {
			throw new ICollectionException("method argument must be an iterable object");
		}
	}
	
	/**
	 * 取得元素的数目
	 *
	 * @return integer
	 * @see count
	 */
	public function size() {
		return $this->count();
	}
	
	/**
	 * 取得一个值在列表中第一次出现的位置
	 * 
	 * 如果列表中不含有此值，则返回-1
	 *
	 * @param mixed $item 值
	 * @return integer
	 */
	public function indexOf($item) {
		$keys = array_keys($this->array, $item);
		if (!empty($keys)) {
			return $keys[0];
		}
		return -1;
	}
	
	/**
	 * 取得一个值在列表中出现的位置数组
	 *
	 * @param mixed $item 值
	 * @return array
	 */
	public function indexesOf($item) {
		$keys = array_keys($this->array, $item);
		return $keys;
	}
	
	/**
	 * 在指定位置插入新的值
	 * 
	 * 原有的值将会被替换掉
	 *
	 * @param integer $index 位置/偏移量
	 * @param mixed $item 值
	 */
	public function insertAt($index, $item) {
		if ($this->offsetExists($index)) {
			$this->array[$index] = $item;
		}
		else {
			throw new ICollectionException("invalid index '{$index}' for the list");
		}
	}
	
	/**
	 * 取得指定位置的值
	 *
	 * @param integer $index 位置/偏移量
	 * @return mixed
	 */
	public function itemAt($index) {
		return $this->offsetGet($index);
	}
	
	/**
	 * 将列表和另一$iterable合并
	 *
	 * @param mixed $iterable array/IIterable
	 */
	public function mergeAll($iterable) {
		if(is_array($iterable) || ($iterable instanceof IIterable))	{
			foreach ($iterable as $k=>$v) {
				if ($this->offsetExists($k)) {
					$this->offsetSet($k, $v);
				}
			}
		}
		else {
			throw new ICollectionException("method argument must be an iterable object");
		}	
	}
	
	/**
	 * 将列表转换为array
	 * 
	 * 实现了 IIterable 对应的方法
	 *
	 * @return array
	 */
	public function toArray() {
		return $this->array;
	}
	
	/**
	 * 判断当前列表是否为空
	 *
	 * @return boolean
	 */
	public function isEmpty() {
		return empty($this->array);
	}
	
	/**
	 * 删除有特定值的元素
	 *
	 * @param mixed $item 元素值
	 */
	public function remove($item) {
		foreach ($this->array as $k=>$v) {
			if ($v == $item) {
				unset($this->array[$k]);
			}
		}
		$this->tidy();
	}
	
	/**
	 * 删除对应位置的元素
	 *
	 * @param integer $index 位置/偏移量
	 */
	public function removeAt($index) {
		$this->offsetUnset($index);
	}
	
	/**
	 * 将列表置为两个iterable对象的差集
	 *
	 * @param mixed $iterable array/IIterable
	 */
	public function removeAll($iterable) {
		if(is_array($iterable))	{
			$this->array = array_diff($this->array, $iterable);
		}
		else if($iterable instanceof IIterable) {
			 $this->array = array_diff($this->array, $iterable->toArray());
		}
		else {
			throw new ICollectionException("method argument must be an iterable object");
		}		
	}
	
	/**
	 * 将列表置为两个iterable对象的交集
	 *
	 * @param mixed $iterable array/IIterable
	 */
	public function retainAll($iterable) {
		if(is_array($iterable))	{
			$this->array = array_intersect($this->array, $iterable);
		}
		else if($iterable instanceof IIterable) {
			 $this->array = array_intersect($this->array, $iterable->toArray());
		}
		else {
			throw new ICollectionException("method argument must be an iterable object");
		}	
	}
	
	/**
	 * 清理不连续的索引
	 *
	 */
	private function tidy() {
		$this->array = array_values($this->array);
	}
}

?>